package com.cx.common.utils;

/** 
* @Author:cxy
* @Date:2019/10/24 15:11
* 
*/


import com.cx.common.thread.ExecutorUtils;

import javax.mail.Address;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public final class UuidUtil {

	private static final AtomicLong SEED_UNIQUIFIER = new AtomicLong(8682522807148012L);

	@SuppressWarnings("unused")
	private static final long MOTHER_OF_MAGIC_NUMBERS = 181783497276652981L;

	@SuppressWarnings("unchecked")
	private static final ThreadLocal<Random> THREAD_LOCAL_UNSECURE_RANDOM = new ThreadLocal() {
		protected Random initialValue() {
			return new Random();
		}
	};

	@SuppressWarnings("unchecked")
	private static final ThreadLocal<SecureRandom> THREAD_LOCAL_SECURE_RANDOM = new ThreadLocal() {
		protected SecureRandom initialValue() {
			return new SecureRandom();
		}
	};

	public static String createClusterUuid() {
		return newUnsecureUuidString();
	}

	public static String createMemberUuid(Address endpoint) {
		return newUnsecureUuidString();
	}

	public static String createClientUuid(Address endpoint) {
		return newUnsecureUuidString();
	}

	public static String newUnsecureUuidString() {
		return newUnsecureUUID().toString();
	}

	public static String newSecureUuidString() {
		return newSecureUUID().toString();
	}

	public static UUID newUnsecureUUID() {
		return getUUID((Random) THREAD_LOCAL_UNSECURE_RANDOM.get());
	}

	public static UUID newSecureUUID() {
		return getUUID((Random) THREAD_LOCAL_SECURE_RANDOM.get());
	}

	public static void main(String[] args) {
		final Set<String> newSetFromMap = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
		final AtomicInteger repeatNum = new AtomicInteger(0);
		final CountDownLatch latch = new CountDownLatch(10);
		for (final AtomicInteger i = new AtomicInteger(0); i.get() <= 10; i.incrementAndGet()) {
			ExecutorUtils.run(new Runnable() {
				@Override
				public void run() {
					int incrementAndGet = i.incrementAndGet();
					for (int j = 0; j < 100000; j++) {
						String createClusterUuid = UuidUtil.createClusterUuid();
						boolean add = newSetFromMap.add(createClusterUuid);
						if (!add) {
							repeatNum.incrementAndGet();
						}
						System.err.println(incrementAndGet + createClusterUuid);
					}
					latch.countDown();
				}
			});
		}
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		ExecutorUtils.shutdown();
		System.err.println("生成了[" + repeatNum.get() + "]个重复UUID");
	}

	private static UUID getUUID(Random random) {
		byte[] data = new byte[16];
		random.nextBytes(data);
		byte[] tmp13_10 = data;
		tmp13_10[6] = ((byte) (tmp13_10[6] & 0xF));
		byte[] tmp23_20 = data;
		tmp23_20[6] = ((byte) (tmp23_20[6] | 0x40));
		byte[] tmp33_30 = data;
		tmp33_30[8] = ((byte) (tmp33_30[8] & 0x3F));
		byte[] tmp43_40 = data;
		tmp43_40[8] = ((byte) (tmp43_40[8] | 0x80));

		long mostSigBits = 0L;
		long leastSigBits = 0L;
		assert (data.length == 16) : "data must be 16 bytes in length";
		for (int i = 0; i < 8; i++) {
			mostSigBits = mostSigBits << 8 | data[i] & 0xFF;
		}
		for (int i = 8; i < 16; i++) {
			leastSigBits = leastSigBits << 8 | data[i] & 0xFF;
		}
		return new UUID(mostSigBits, leastSigBits);
	}

	private static long seedUniquifier() {
		for (;;) {
			long current = SEED_UNIQUIFIER.get();
			long next = current * 181783497276652981L;
			if (SEED_UNIQUIFIER.compareAndSet(current, next)) {
				return next;
			}
		}
	}
}
